package Sdo.terminalfx;

 import com.jcraft.jsch.ChannelShell;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.ProxyHTTP;
import com.jcraft.jsch.ProxySOCKS4;
import com.jcraft.jsch.ProxySOCKS5;
import com.jcraft.jsch.Session;
 import com.pty4j.PtyProcess;
import com.pty4j.WinSize;
import Sdo.terminalfx.annotation.WebkitCall;
import Sdo.terminalfx.config.TerminalConfig;
import Sdo.terminalfx.helper.ThreadHelper;

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;

import org.apache.commons.lang3.SystemUtils;
import org.apache.log4j.Logger;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.LinkedBlockingQueue;

public class Terminal extends TerminalViewX {
	private static Logger logger = Logger.getLogger(Terminal.class);  

	private PtyProcess process;
	private final ObjectProperty<Writer> outputWriterProperty;
	private final Path terminalPath;
	private String[] termCommand;
	private final LinkedBlockingQueue<String> commandQueue;
	private Boolean bashflag=false;
	TerminalConfig terminalConfig;
	ProgressBar pbar;
	Label systemInfo;

    int col=1000;//列
    int row=100;//行
    int wp=500;//宽度
    int hp=500;//高度

    
	Session session = null;
	ChannelShell channelShell;
	private static final int TIME_OUT = 3*60*1000; //设置超时为5分钟
	

	public Terminal() {
		this(null, null,null,null);
	
	}

	public void setInfoText(String info) {
	
		systemInfo.setManaged(true);
		systemInfo.setVisible(true);
		systemInfo.setText(info);
//		Timer t = new Timer();
//		t.schedule(new TimerTask() {
//
//			@Override
//			public void run() {
//				// TODO Auto-generated method stub
//				systemInfo.setManaged(false);
//				systemInfo.setVisible(false);
//				//System.out.println("自动消失");
//				t.cancel();
//			}
//		}, 5000, 1);

	}
	public Terminal(TerminalConfig terminalConfig, Path terminalPath,ProgressBar pbar,Label systemInfo) {
		this.pbar=pbar;
		this.systemInfo=systemInfo;
		setTerminalConfig(terminalConfig);
		
		// 获取用户信息
		this.terminalConfig = terminalConfig;

		this.terminalPath = terminalPath;

		outputWriterProperty = new SimpleObjectProperty<Writer>();
		commandQueue = new LinkedBlockingQueue<String>();
		
	}

	@WebkitCall
	public void command(String command) {
		
		try {
			commandQueue.put(command);
		} catch (final InterruptedException e) {
			throw new RuntimeException(e);
		}
		ThreadHelper.start(() -> {
			
			try {

				final String commandToExecute = commandQueue.poll();
				
				getOutputWriter().write(commandToExecute);
				
				//logger.debug("在输入");
				
				
				try {
			//		logger.debug("session状态"+session.isConnected());
			//		logger.debug("channelShell状态"+channelShell.isConnected());
			//		logger.debug("channelShell状态"+channelShell.isClosed());
					if(bashflag) {
						//logger.debug("bash");
					}else {
					//异常退出重新进一次
					if(!session.isConnected()){
						session.disconnect();
						channelShell.disconnect();
						initializeSsh();
					}else if(channelShell.isClosed()) {
						//直接退出,进Bash
						bashflag=true;
						session.disconnect();
						initializeProcess();
					}
					}
					
				} catch (Exception e1) {
					// TODO Auto-generated catch block
					//logger.debug("当前是bash");
				}
			
				getOutputWriter().flush();

			} catch (final IOException e) {
				try {
					channelShell.disconnect();
					session.disconnect();
				} catch (Exception e2) {
					// TODO: handle exception
				}
			
				
			} 
		});

	}

	@Override
	public void onTerminalReady() {
		ThreadHelper.start(() -> {
			
				
				this.getDataDir();
				
				if (!"".equals(terminalConfig.getHostname()) && !"".equals(terminalConfig.getUsername())) {
					
			       
					try {
						ThreadHelper.runActionLater(() -> {
							pbar.setManaged(true);
							pbar.setVisible(true);
							
						});
						initializeSsh();
					
					} catch (JSchException e) {
						// TODO Auto-generated catch block
						ThreadHelper.runActionLater(() -> {
							pbar.setManaged(false);
							pbar.setVisible(false);
							
							setInfoText("建立链接超时，请检查链接信息"+e.getMessage());
							
					});

					
						
						
						e.printStackTrace();
					} catch (IOException e) {
						ThreadHelper.runActionLater(() -> {
							pbar.setVisible(false);
							pbar.setManaged(false);
							
							setInfoText("建立链接出错"+e.getMessage());
					});

						// TODO Auto-generated catch block
						e.printStackTrace();
						
					}
				
				} else {
					try {
						initializeProcess();
					} catch (Exception e) {
						// TODO Auto-generated catch block
						ThreadHelper.runActionLater(() -> {
							this.pbar.setVisible(false);
							this.pbar.setManaged(false);
							
							setInfoText("创建终端失败"+e.getMessage());
							
//							Notifications noca = Notifications.create().title("创建终端失败").text(e.getMessage()).graphic(null)
//									.hideAfter(Duration.seconds(10)).position(Pos.TOP_RIGHT)
//									.onAction(new EventHandler<ActionEvent>() {
//										@Override
//										public void handle(ActionEvent event) {
//											// TODO Auto-generated method stub
//											System.out.println("创建终端失败");
//										}
//									});
//							noca.showConfirm();
					});

						e.printStackTrace();
					}
				}

			 
		});
	}

	public String getPwd()  {
		
		return path;
	}
	public String getSelect()  {
		
		return ptext;
	}
	private void initializeSsh() throws JSchException, IOException {
		
		JSch jsch=new JSch();

		session = jsch.getSession(terminalConfig.getUsername(), terminalConfig.getHostname().trim(), terminalConfig.getPort());
	
		 //支持密钥的方式登陆，只需在jsch.getSession之前设置一下密钥的相关信息就可以了  
        if (terminalConfig.getPemFile() != null && !"".equals(terminalConfig.getPemFile())&& !"选择密钥".equals(terminalConfig.getPemFile())) {  
               if (terminalConfig.getPassword() != null && !"".equals(terminalConfig.getPassword())) {  
                //设置带口令的密钥
                   jsch.addIdentity(terminalConfig.getPemFile(), terminalConfig.getPassword());  
               } else {
                //设置不带口令的密钥  
                   jsch.addIdentity(terminalConfig.getPemFile());  
               }
        }else {
//        	logger.debug("密码登陆>>>>"+terminalConfig.getUsername());
//			logger.debug("密码登陆>>>>"+terminalConfig.getPassword());

        	session.setPassword(terminalConfig.getPassword());
    		
        }

		session.setConfig("StrictHostKeyChecking", "no");//第一次访问服务器不用输入yes
		session.setTimeout(TIME_OUT);
		

		if (terminalConfig.getProxyType() != null)
		{
			if ("HTTP".equalsIgnoreCase(terminalConfig.getProxyType()))
			{
				ProxyHTTP proxyHTTP = new ProxyHTTP(terminalConfig.getProxyIp(),Integer.parseInt(terminalConfig.getProxyPort()));
//				logger.debug("proxyHTTP>>>>"+terminalConfig.getProxyIp());
//				logger.debug("proxyHTTP>>>>"+terminalConfig.getProxyPort());
				
				if (terminalConfig.getProxyUser()!=null&&!"".equals(terminalConfig.getProxyUser())) {
					proxyHTTP.setUserPasswd(terminalConfig.getProxyUser(), terminalConfig.getProxyPassword());
					
//
//					logger.debug("proxyHTTP>>>>"+terminalConfig.getProxyUser());
//					logger.debug("proxyHTTP>>>>"+terminalConfig.getProxyPassword());
				}
			

				session.setProxy(proxyHTTP);


			}
			else if ("SOCKS5".equalsIgnoreCase(terminalConfig.getProxyType()))
			{
				ProxySOCKS5 ProxySOCKS5=new ProxySOCKS5(terminalConfig.getProxyIp(),Integer.parseInt(terminalConfig.getProxyPort()));
//				logger.debug("ProxySOCKS5>>>>"+terminalConfig.getProxyIp());
//				logger.debug("ProxySOCKS5>>>>"+terminalConfig.getProxyPort());
				if (terminalConfig.getProxyUser()!=null&&!"".equals(terminalConfig.getProxyUser())) {
					ProxySOCKS5.setUserPasswd(terminalConfig.getProxyUser(), terminalConfig.getProxyPassword());
				
//					logger.debug("ProxySOCKS5>>>>"+terminalConfig.getProxyUser());
//					logger.debug("ProxySOCKS5>>>>"+terminalConfig.getProxyPassword());
					
					
				}
				session.setProxy(ProxySOCKS5);

			}
			else if ("SOCKS4".equalsIgnoreCase(terminalConfig.getProxyType()))
			{
				ProxySOCKS4 ProxySOCKS4=new ProxySOCKS4(terminalConfig.getProxyIp(), Integer.parseInt(terminalConfig.getProxyPort()));
				if (terminalConfig.getProxyUser()!=null&&!"".equals(terminalConfig.getProxyUser())) {
					ProxySOCKS4.setUserPasswd(terminalConfig.getProxyUser(), terminalConfig.getProxyPassword());

//					logger.debug("ProxySOCKS4>>>>"+terminalConfig.getProxyUser());
//					logger.debug("ProxySOCKS4>>>>"+terminalConfig.getProxyPassword());
					
				}
				session.setProxy(ProxySOCKS4);
			}else {
				//System.out.println("没有任何代理");
			}
		}
		
		 //session.setConfig("StrictHostKeyChecking", "no");
		 //session.setConfig("PreferredAuthentications", "password");
//		    session.setServerAliveInterval(40000);
//		    session.setTimeout(120000);
//		    session.setDaemonThread(true);
//		    session.connect(30000);
		
		session.disconnect();
		session.connect();
		
		channelShell = (ChannelShell) session.openChannel("shell");
		//设置链接属性
		 

		col=getColumns();
		row=getRows();
		wp=(int)getWidth();
		hp=(int)getHeight();
			//channelShell.setEnv("LANG", "zh_CN.UTF8");
			//channelShell.setAgentForwarding(false);
			//channelShell.setPtyType("xterm");
			//channelShell.connect(10000);
			channelShell.setPtySize(col, row, wp, hp);
			//channelShell.setPty(true);
			channelShell.connect();

			columnsProperty().addListener(evt -> updateWinSize());
			rowsProperty().addListener(evt -> updateWinSize());
			updateWinSize();
			setInputReader(new BufferedReader(new InputStreamReader(channelShell.getInputStream())));
			setErrorReader(new BufferedReader(new InputStreamReader(channelShell.getExtInputStream())));
			setOutputWriter(new BufferedWriter(new OutputStreamWriter(channelShell.getOutputStream())));
			
			focusCursor();
		
			countDownLatch.countDown();
		

	}

	private void initializeProcess() throws Exception {

		final Path dataDir = getDataDir();

		if (SystemUtils.IS_OS_WINDOWS) {
			this.termCommand = getTerminalConfig().getWindowsTerminalStarter().split("\\s+");
		} else {
			this.termCommand = getTerminalConfig().getUnixTerminalStarter().split("\\s+");
		}

		final Map<String, String> envs = new HashMap<>(System.getenv());
		envs.put("TERM", "xterm");

		System.setProperty("PTY_LIB_FOLDER", dataDir.resolve("libpty").toString());

		if (Objects.nonNull(terminalPath) && Files.exists(terminalPath)) {
			this.process = PtyProcess.exec(termCommand, envs, terminalPath.toString());
		} else {
			this.process = PtyProcess.exec(termCommand, envs);
		}

		columnsProperty().addListener(evt -> updateWinSize());
		rowsProperty().addListener(evt -> updateWinSize());
		updateWinSize();

		setInputReader(new BufferedReader(new InputStreamReader(process.getInputStream())));
		setErrorReader(new BufferedReader(new InputStreamReader(process.getErrorStream())));
		setOutputWriter(new BufferedWriter(new OutputStreamWriter(process.getOutputStream())));
		focusCursor();

		countDownLatch.countDown();

		process.waitFor();

		// */
	}

	private Path getDataDir() {
		final String userHome = System.getProperty("user.home");
		final Path dataDir = Paths.get(userHome).resolve(".terminalfx");
		return dataDir;
	}

	public Path getTerminalPath() {
		return terminalPath;
	}

	private void updateWinSize() {
		try {
			process.setWinSize(new WinSize(getColumns(), getRows()));
		} catch (Exception e) {
			//
		}
	}
	public void resizeTerminal(int columns, int rows) {

		col=columns;
		row=rows;
		wp=(int)getWidth();
		hp=(int)getHeight();
//		System.out.println(">>>>>屏幕调整了大小"+wp);
//		System.out.println(">>>>>屏幕调整了大小"+hp);
		//设置新的界面大小
		channelShell.setPtySize(col, row, wp, hp);
		
	}
	public ObjectProperty<Writer> outputWriterProperty() {
		return outputWriterProperty;
	}

	public Writer getOutputWriter() {
		return outputWriterProperty.get();
	}

	public void setOutputWriter(Writer writer) {
		outputWriterProperty.set(writer);
		ThreadHelper.runActionLater(() -> {
			
			//获取到光标后注销链接进度条
			pbar.setVisible(false);
			pbar.setManaged(false);
		}, true);
	}

	public PtyProcess getProcess() {
		return process;
	}

}
